import { isValidCron } from 'cron-validator';
import * as nodeCron from 'node-cron';
import DateUtil from '@/utils/javaScript/date.util';

// 定义node-cron的ScheduleOptions接口
interface ScheduleOptions {
  scheduled?: boolean;
  timezone?: string;
}

/**
 * Cron表达式工具类
 */
class CronUtils {
  /**
   * 验证Cron表达式是否有效
   * @param cronExpression Cron表达式
   * @returns 是否有效
   */
  public static isValid(cronExpression: string): boolean {
    try {
      // 允许6位（含秒）格式
      return isValidCron(cronExpression, { seconds: true }) && nodeCron.validate(cronExpression);
    } catch (error) {
      return false;
    }
  }

  /**
   * 获取下一次执行时间
   * @param cronExpression Cron表达式
   * @returns 下一次执行时间
   */
  public static getNextExecution(cronExpression: string): Date | null {
    if (!this.isValid(cronExpression)) {
      return null;
    }

    try {
      // 创建当前时间的副本
      const now = DateUtil.getNowDate();
      
      // 由于node-cron的ScheduledTask没有nextDate方法
      // 我们需要手动计算下一次执行时间
      let nextExecutionTime: Date | null = null;
      
      const task = nodeCron.schedule(cronExpression, () => {
        if (!nextExecutionTime) {
          nextExecutionTime = DateUtil.getNowDate();
        }
      }, {
        scheduled: false // 不立即启动任务
      } as ScheduleOptions);
      
      // 手动计算下一次执行时间
      // 从当前时间开始，按分钟递增，直到找到下一个匹配时间
      let testTime = new Date(now);
      let found = false;
      
      // 最多查找未来7天内的时间点
      for (let i = 0; i < 7 * 24 * 60 && !found; i++) {
        // 增加1分钟 (DateUtil没有直接提供增加分钟的方法，使用原生方法)
        testTime.setMinutes(testTime.getMinutes() + 1);
        
        // 检查此时间是否匹配cron表达式
        const parts = cronExpression.split(' ');
        const minute = testTime.getMinutes();
        const hour = testTime.getHours();
        const dayOfMonth = testTime.getDate();
        const month = testTime.getMonth() + 1; // JavaScript月份从0开始
        const dayOfWeek = testTime.getDay(); // 周日是0
        
        // 简单检查是否匹配（仅支持基本情况）
        if (this.matchesCronPart(minute, parts[1]) && 
            this.matchesCronPart(hour, parts[2]) && 
            this.matchesCronPart(dayOfMonth, parts[3]) && 
            this.matchesCronPart(month, parts[4]) && 
            (parts[5] === '*' || this.matchesCronPart(dayOfWeek, parts[5]))) {
          found = true;
          nextExecutionTime = new Date(testTime);
        }
      }
      
      task.stop();
      
      return nextExecutionTime;
    } catch (error) {
      return null;
    }
  }
  
  /**
   * 检查数值是否匹配cron表达式的一部分
   * @param value 数值
   * @param cronPart cron表达式的一部分
   * @returns 是否匹配
   */
  private static matchesCronPart(value: number, cronPart: string): boolean {
    if (cronPart === '*') {
      return true;
    }
    
    // 处理逗号分隔的值
    if (cronPart.includes(',')) {
      return cronPart.split(',').some(part => this.matchesCronPart(value, part));
    }
    
    // 处理范围
    if (cronPart.includes('-')) {
      const [start, end] = cronPart.split('-').map(Number);
      return value >= start && value <= end;
    }
    
    // 处理步长
    if (cronPart.includes('/')) {
      const [range, step] = cronPart.split('/');
      const stepValue = parseInt(step, 10);
      
      if (range === '*') {
        return value % stepValue === 0;
      }
      
      // 如果范围是数字
      if (!isNaN(parseInt(range, 10))) {
        const start = parseInt(range, 10);
        return (value - start) % stepValue === 0 && value >= start;
      }
    }
    
    // 直接匹配数字
    return value === parseInt(cronPart, 10);
  }
}

export default CronUtils; 